package com.enhe.gck.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.enhe.gck.calculate.WeightErrorCalculation;
import com.enhe.gck.com.oracle.LwdHttpConfig;
import com.enhe.gck.communal.*;
import com.enhe.gck.controller.dto.WeightingDTO;
import com.enhe.gck.controller.vo.CaptureImageVO;
import com.enhe.gck.controller.vo.ClientInvokingRespVO;
import com.enhe.gck.controller.vo.InstallationLaneVO;
import com.enhe.gck.controller.vo.WeightPrintInfoVO;
import com.enhe.gck.enums.WeighingResultEnum;
import com.enhe.gck.exception.BusinessException;
import com.enhe.gck.exception.MySqlException;
import com.enhe.gck.exception.WeighException;
import com.enhe.gck.item.OrderHWANE16677Item;
import com.enhe.gck.item.PouringOrderHWANE18490Item;
import com.enhe.gck.item.SupplyOrderHWANE16829Item;
import com.enhe.gck.pojo.*;
import com.enhe.gck.utils.DateUtil;
import com.enhe.gck.utils.LockUtil;
import com.kuangkie.carbon.common.BytesInfoVO;
import com.kuangkie.carbon.common.IntegrationMsg;
import com.kuangkie.carbon.panel.CarbonPanel;
import com.kuangkie.carbon.record.FGRecordBuilder;
import com.kuangkie.carbon.record.FGRecordComplexusBuilder;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static com.enhe.gck.CarbonBaseConstant.*;
import static com.enhe.gck.CarbonEnumKeyValue.*;
import static com.enhe.gck.communal.ClientCommand.*;
import static com.enhe.gck.constant.FrontPrompt.*;
import static com.enhe.gck.item.WeighHWANE16720Item.*;

/**
 * 前置机调用处理service
 */
@Slf4j
@Service
@AllArgsConstructor
public class GckFrontService {

    private final Object lockObj = new Object();

    private VehicleOrderService vehicleOrderService;
    private WeighService weighService;
    private ConfigService configService;
    private CarService carService;
    private BatchService batchService;
    private BatchRecordService batchRecordService;
    private WovenBagRecordService wovenBagRecordService;

    /**
     * 根据提货码获取订单信息
     *
     * @param pickupCode 提货码
     * @param onlyRead   仅用于查看标记
     * @return Resp<OrderCommon>
     */
    public Resp<OrderCommon> getOrderInfoByPickupCode(String pickupCode, Boolean onlyRead) {
        // TODO cb 后期删除
        CarbonPanel.changeCurrentProgram(366042330866425862L);

        log.warn("[GCK_Front]前置机调用获取订单信息[请求] => 提货码:[{}],onlyRead:[{}]", pickupCode, onlyRead);
        Optional<OrderCommon> orderOptional = vehicleOrderService.getNoCompletedOrderByPickupCode(pickupCode);
        if (!orderOptional.isPresent()) {
            Resp<OrderCommon> fail = Resp.fail("订单不存在");
            log.warn("[GCK_Front]前置机调用获取订单信息[响应] => {}", fail);
            return fail;
        }
        OrderCommon order = orderOptional.get();

        // 如果订单已绑定ic卡则不返回
        if (Boolean.FALSE.equals(onlyRead) && StrUtil.isNotBlank(order.getIcCard())) {
            log.warn("[GCK_Front]前置机调用获取订单信息[响应] => 订单已绑定IC卡");
            Resp<OrderCommon> fail = Resp.fail("订单已绑定IC卡");
            log.warn("[GCK_Front]前置机调用获取订单信息[响应] => [{}]", JSONUtil.toJsonStr(fail));
            return fail;
        }

        Resp<OrderCommon> success = Resp.success(order);
        log.warn("[GCK_Front]前置机调用获取订单信息[响应] => [{}]", JSONUtil.toJsonStr(success));
        return success;
    }

    public Resp<List<OrderCommon>> getOrderInfoList(String carNo, String orderType) {
        Optional<Config> limitDayOptional = configService.getConfig(参数配置_类型_补打限制天数_bdxzts);
        if (!limitDayOptional.isPresent()) {
            throw new BusinessException("补打限制天数未配置");
        }
        int limitDay = Integer.parseInt(limitDayOptional.get().getValue());
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN);
        String startTimeStr = dateTimeFormatter.format(LocalDateTime.now().minusDays(limitDay));
        String endTimeStr = dateTimeFormatter.format(LocalDateTime.now());

        log.warn("[GCK_Front]根据车号及日期获取打印单列表信息[请求] => 车牌号:{},订单类型:{},开始时间:{},结束时间:{}", carNo, orderType, startTimeStr, endTimeStr);

        if (!Objects.equals(PublicConstant.OrderTypeSale, orderType) && !Objects.equals(PublicConstant.OrderTypeSupply, orderType)) {
            throw new BusinessException("订单类型错误");
        }
        List<OrderCommon> orderList = vehicleOrderService.getCompletedOrderByCarNoOrderType(carNo, orderType, startTimeStr, endTimeStr);
        if (CollectionUtil.isEmpty(orderList)) {
            return Resp.success(Collections.emptyList());
        }

        log.warn("[GCK_Front]前置机调用执行命令根据ic卡号获取订单打印信息[响应] => [{}]", JSONUtil.toJsonStr(orderList));
        return Resp.success(orderList);
    }

    /**
     * 根据车牌号获取可补打提货单的订单列表信息
     *
     * @param carNo     车牌号
     * @param orderType 订单类型(暂时只有销售订单有提货单 Sale)
     * @return Resp<List < OrderCommon>>
     */
    public Resp<List<OrderCommon>> getBillOfLoadingOrderList(String carNo, String orderType) {
        log.warn("[GCK_Front]前置机调用获取订单信息[请求] => 车牌号:[{}],订单类型:[{}]", carNo, orderType);
        if (!Objects.equals(PublicConstant.OrderTypeSale, orderType)) {
            return Resp.fail("暂仅支持销售订单!");
        }
        List<String> stateList = Arrays.asList(String.valueOf(订单记录_状态_已创建_ycj), String.valueOf(订单记录_状态_待过一磅_dgyb), String.valueOf(订单记录_状态_待过二磅_dgeb));
        List<OrderCommon> orderList = vehicleOrderService.getSaleOrderList(stateList, carNo);
        log.warn("[GCK_Front]前置机调用获取订单信息[响应] => {}", JSONUtil.toJsonStr(orderList));
        return Resp.success(orderList);
    }

    /**
     * 根据车牌号获取可补打【原材料验收单】的订单列表信息
     *
     * @param carNo 车牌号
     * @return resp
     */
    public Resp<List<OrderCommon>> getReceivingReportOrderList(String carNo) {
        log.warn("[GCK_Front]前置机调用获取可补打【原材料验收单】订单信息[请求] => 车牌号:[{}]", carNo);
        List<String> stateList = Arrays.asList(String.valueOf(订单记录_状态_已创建_ycj), String.valueOf(订单记录_状态_待过一磅_dgyb), String.valueOf(订单记录_状态_待过二磅_dgeb));
        List<OrderCommon> orderList = vehicleOrderService.getReceivingReportOrderList(stateList, carNo);
        log.warn("[GCK_Front]前置机调用获取可补打【原材料验收单】订单信息[响应] => [{}]", JSONUtil.toJsonStr(orderList));
        return Resp.success(orderList);
    }

    /**
     * 将ic卡号绑定订单
     *
     * @param orderId     订单主键id
     * @param icCardNo    ic卡号
     * @param orderTypeEn 订单类型(en)
     * @return resp
     */
    public Resp<WeightPrintInfoVO> bindCard(Long orderId, String icCardNo, String orderTypeEn) {
        log.warn("[GCK_Front]前置机调用绑定IC卡[请求] => 订单id:[{}], ic卡号:[{}], 订单类型:[{}]", orderId, icCardNo, orderTypeEn);

        // 判断ic卡是否占用
        Optional<OrderCommon> existOrderOptional = vehicleOrderService.getNoCompletedOrderByIcId(icCardNo);
        if (existOrderOptional.isPresent()) {
            Resp<WeightPrintInfoVO> fail = Resp.fail("IC卡已被占用,占用订单号 => [{" + existOrderOptional.get().getOrder() + "}]");
            log.warn("[GCK_Front]前置机调用绑定IC卡[响应] => [{}]", JSONUtil.toJsonStr(fail));
            return fail;
        }

        Optional<OrderCommon> orderOptional = vehicleOrderService.getNoCompletedOrderById(orderId);
        if (!orderOptional.isPresent()) {
            Resp<WeightPrintInfoVO> fail = Resp.fail(Order_Information_Does_Not_Exist);
            log.warn("[GCK_Front]前置机调用绑定IC卡[响应] => [{}]", JSONUtil.toJsonStr(fail));
            return fail;
        }
        OrderCommon order = orderOptional.get();

        String orderNo = order.getOrder();

        WeightPrintInfoVO printInfoVO = getWeightPrintInfoVO(order);

        String tableKey;
        String icCardColumKey;
        String statusKey;

        if (Objects.equals(PublicConstant.OrderTypeSale, orderTypeEn)) {
            tableKey = 订单记录_ddjl;
            icCardColumKey = OrderHWANE16677Item.基本属性组_IC卡号;
            statusKey = OrderHWANE16677Item.基本属性组_状态;
        } else if (Objects.equals(PublicConstant.OrderTypeSupply, orderTypeEn)) {
            tableKey = 供货订单_ghdd;
            icCardColumKey = SupplyOrderHWANE16829Item.基本属性组_IC卡号;
            statusKey = SupplyOrderHWANE16829Item.基本属性组_状态;
        } else {
            return Resp.fail("订单类型错误");
        }

        IntegrationMsg msg = CarbonPanel.getFGRecordBuilder(tableKey, String.valueOf(orderId))
                .putAttribute(icCardColumKey, icCardNo)
                .putAttribute(statusKey, 订单记录_状态_待过一磅_dgyb)
                .integrate();
        if (!msg.success()) {
            Resp<WeightPrintInfoVO> fail = Resp.fail("IC卡绑定失败,原因 => [" + msg.getRefuseMsgContentStr() + "]");
            log.error("[GCK_Front]前置机调用绑定IC卡[响应] => [{}]", fail);
            return fail;
        }

        Resp<WeightPrintInfoVO> success = Resp.success("IC卡绑定成功", printInfoVO);
        log.warn("[GCK_Front]前置机调用绑定IC卡[响应] => [{}]", JSONUtil.toJsonStr(success));
        return success;
    }

    private static WeightPrintInfoVO getWeightPrintInfoVO(OrderCommon order) {
        // 是销售订单
        boolean isSaleOrder = PublicConstant.OrderTypeSale.equals(order.getOrderTypeEn());
        boolean isSupplyOrder = PublicConstant.OrderTypeSupply.equals(order.getOrderTypeEn());
        // 是散装
        boolean isBulk = PublicConstant.ProductPackageTypeBulk.equals(order.getPackTypeEN());
        boolean isBag = PublicConstant.ProductPackageTypeBag.equals(order.getPackTypeEN());

        WeightPrintInfoVO printInfoVO = new WeightPrintInfoVO();
        // 车牌号
        printInfoVO.setCarNumber(order.getVehicle());
        // 设置产品规格
        printInfoVO.setProductSpec(order.getMaterialName());
        // 设置包装方式
        printInfoVO.setPackTypeZH(order.getPackTypeZH());
        printInfoVO.setPackTypeEN(order.getPackTypeEN());
        // 设置重量单位
        printInfoVO.setWeightUnit(isSaleOrder ? (isBulk ? "吨" : "袋") : "吨");

        // 设置原材料验收单信息
        if (isSupplyOrder) {
            // 单据号
            printInfoVO.setDocumentNumber(printInfoVO.getOrderNo());
            // 单据日期
//            printInfoVO.setDate(DateUtil.dateToString(CarbonPanel.getRecordDiscover(供货订单_ghdd).getRecord(String.valueOf(order.getId())).getDate(PublicMethod.createTimeStr(供货订单_ghdd))));
            // 应要求，销售订单打印日期改为当前时间！！！
            printInfoVO.setDate(DateUtil.DateTimeToString(LocalDateTime.now()));
            // 设置供应商
            printInfoVO.setSupplier(order.getCompanyName());
            // 设置物料名称
            printInfoVO.setMaterialName(order.getMaterialName());
            printInfoVO.setMaterialPlace(order.getMaterialPlace());
        }

        // 设置订单数量
        printInfoVO.setQuantity(isSaleOrder && isBag ? order.getBags() : Optional.ofNullable(order.getWeight()).orElse(BigDecimal.ZERO).intValue());
        printInfoVO.setOrderNo(order.getOrder());
        printInfoVO.setOrderTypeEn(order.getOrderTypeEn());
        printInfoVO.setOrderTypeZh(order.getOrderTypeZh());
        printInfoVO.setCompanyName(order.getCompanyName());
        // 销售订单提货人
        printInfoVO.setConsignee(isSaleOrder ? order.getDriver() : null);
        return printInfoVO;
    }

    public Resp<WeightPrintInfoVO> billOfLoadingOrderPatchwork(Long orderId) {
        log.warn("[GCK_Front]前置机调用补打提货单[请求] => 订单id:[{}], 订单类型:[{}]", orderId, PublicConstant.OrderTypeSale);
        Optional<OrderCommon> orderOptional = vehicleOrderService.getNoCompletedOrderById(orderId);
        if (!orderOptional.isPresent()) {
            return Resp.fail(Order_Information_Does_Not_Exist);
        }
        OrderCommon order = orderOptional.get();
        WeightPrintInfoVO weightPrintInfoVO = getWeightPrintInfoVO(order);
        Resp<WeightPrintInfoVO> resp = Resp.success("补打提货单成功", weightPrintInfoVO);
        log.warn("[GCK_Front]前置机调用补打发货单[响应] => {}", resp);
        return resp;
    }

    /**
     * 根据订单id执行前置机执行的命令(上磅,称重,装车)
     *
     * @param param 前置机监听参数
     * @return resp
     */
    @Transactional(rollbackFor = Exception.class)
    public Resp<ClientInvokingRespVO> executeCommand(ClientInvokingParam param) {
        // TODO cb 后期删除
        CarbonPanel.changeCurrentProgram(366042330866425862L);
        String logPrefix = "[前置机][" + param.getCommand() + "],IC卡号:[" + param.getIcId() + "],车牌号：[" + param.getCarNumber() + "]";

        log.warn("{}请求参数:[{}]", logPrefix, JSONUtil.toJsonStr(param));
        OrderCommon order = null;
        if (!Objects.equals(LOAD_COUNT_MODIFY, param.getCommand()) && !Objects.equals(LOAD_LAST_COUNT_CHECK, param.getCommand())) {
            // 参数校验并获取订单
            order = checkParamAndReturnOrder(param);
            logPrefix += ",订单号:[" + order.getOrder() + "]";
        }
        // 返回参数
        Resp<ClientInvokingRespVO> resp;

        switch (param.getCommand()) {
            // [上磅]
            case UP_WEIGHT:
                resp = onThePoundProcess(logPrefix, order);
                break;
            // [称重]
            case WEIGHT:
                resp = weightProcess(logPrefix, order, param);
                break;
            // [装车]
            case LOAD:
                resp = loadProcess(logPrefix, order, param);
                break;
            // [装车数量修改]
            case LOAD_COUNT_MODIFY:
                resp = loadCountModifyProcess(logPrefix, param);
                break;
            case LOAD_LAST_COUNT_CHECK:
                resp = lastLoadingCheckProcess(logPrefix, param.getIcCardList());
                break;
            // [装车完成]
            case LOAD_COMPLETE:
                resp = loadCompleteProcess(order, param.getIcId(), param.getIsLoadComplete());
                break;
            default:
                throw new BusinessException("前置机执行的命令不存在");
        }

        log.warn("{}响应参数:[{}]", logPrefix, JSONUtil.toJsonStr(resp));
        return resp;
    }

    private Resp<ClientInvokingRespVO> lastLoadingCheckProcess(String logPrefix, List<String> icCardList) {
        if (CollectionUtil.isEmpty(icCardList)) {
            return Resp.fail(No_Order_Available);
        }
        icCardList.forEach(icCard -> {
            Optional<OrderCommon> orderOptional = vehicleOrderService.getNoCompletedSaleOrderByIcId(icCard);
            if (orderOptional.isPresent()) {
                OrderCommon order = orderOptional.get();
                String logPrefixFinal = logPrefix + ",订单号:[" + order.getOrder() + "]";
                // 实际装货量 != 上次装货量 时修改
                if (!Objects.equals(order.getActualBags(), order.getLastLoadingVolume())) {
                    // 更新订单状态
                    IntegrationMsg msg = CarbonPanel.getFGRecordBuilder(order.getOrderTableKey(), String.valueOf(order.getId()))
                            // 更新上次实际装货量
                            .putAttribute(order.getLastLoadingVolumeKey(), order.getActualBags())
                            .putAttribute(OrderHWANE16677Item.基本属性组_命令, 操作命令_装车触发_zccf) // 设置改命令用于不验证费用等逻辑
                            .integrate();
                    if (!msg.success()) {
                        log.error("{}修改订单实际装货量失败,原因 => [{}]", logPrefixFinal, msg.getRefuseMsgContentStr());
                    } else {
                        log.warn("{}修改订单实际装货量成功", logPrefixFinal);
                    }
                }
            }
        });
        return Resp.success("校验订单上次装货量成功!");
    }

    private Resp<ClientInvokingRespVO> loadCountModifyProcess(String logPrefix, ClientInvokingParam param) {
        Optional<OrderCommon> orderOptional = vehicleOrderService.getNoCompletedSaleOrderByIcId(param.getIcId());
        if (!orderOptional.isPresent()) {
            throw new BusinessException(No_Order_Available);
        }
        OrderCommon order = orderOptional.get();
        logPrefix += ",订单号:[" + order.getOrder() + "]";
        if (null == param.getLoadedNum() || param.getLoadedNum().compareTo(BigDecimal.ZERO) == 0) {
            log.error("{}装车数量不能为空", logPrefix);
            throw new BusinessException(Load_Number_Not_Empty);
        }

        if (!Objects.equals(订单记录_状态_待过二磅_dgeb, Long.valueOf(order.getState()))
                && !Objects.equals(PublicConstant.ProductPackageTypeBag, order.getPackTypeEN())) {
            log.error("{}订单状态不是待过二磅or订单类型不是袋装,不允许修改装车量", logPrefix);
            throw new BusinessException(Load_Not_Allow_Modify_Loading_Quantity);
        }

        // 实际装车量=传的装车量 + 上次最终装货量
        int actualLoading = param.getLoadedNum().intValue() + Optional.ofNullable(order.getLastLoadingVolume()).orElse(0);
        FGRecordBuilder builder = CarbonPanel.getFGRecordBuilder(order.getOrderTableKey(), String.valueOf(order.getId()))
                .putAttribute(OrderHWANE16677Item.基本属性组_实际袋装数, actualLoading)
                .putAttribute(OrderHWANE16677Item.基本属性组_命令, 操作命令_装车触发_zccf);
        FGRecordComplexusBuilder complexusBuilder = CarbonPanel.getFGRecordComplexusBuilder().putRecord(builder.getRecord());
        // 记录编织袋“使用”记录
        wovenBagRecordService.getWovenBagRecord(order.getId(), order.getOrder(), param.getIncrease(), 编织袋记录类型_使用_sy)
                .ifPresent(
                        wovenBagRecord -> {
                            WovenBagUseRecordTemp wovenBagUseRecordTemp = new WovenBagUseRecordTemp();
                            wovenBagUseRecordTemp.setOrderId(wovenBagRecord.getOrderId());
                            wovenBagUseRecordTemp.setNumber(wovenBagRecord.getNumber());
                            wovenBagUseRecordTemp.setWovenBagId(wovenBagRecord.getWovenBagId());
                            log.warn("编织袋使用记录（Temp）：{}", JSON.toJSONString(wovenBagUseRecordTemp));
                            complexusBuilder.putBean(wovenBagUseRecordTemp);
                        }
                );
        IntegrationMsg msg = complexusBuilder.integrate();
        if (!msg.success()) {
            log.error("{}装车数量修改失败,原因:[{}]", logPrefix, msg.getRefuseMsgContentStr());
            throw new MySqlException(Load_Modify_Loading_Quantity_Fail);
        } else {
            log.warn("{}装车数量修改成功;实际装车量:[{}],传入装车量:[{}],上次最终装货量:[{}]", logPrefix, actualLoading, param.getLoadedNum(), order.getLastLoadingVolume());
        }

        ClientInvokingRespVO resp = new ClientInvokingRespVO();
        resp.setOrderNo(order.getOrder());
        resp.setCarNumber(order.getVehicle());
        resp.setLastLoading(param.getLoadedNum().intValue());

        Resp<ClientInvokingRespVO> success = Resp.success(Load_Modify_Loading_Quantity_Success);
        success.setData(resp);
        return success;
    }

    /**
     * 装车完成处理
     *
     * @param order          订单
     * @param icId
     * @param isLoadComplete
     * @return resp
     */
    private Resp<ClientInvokingRespVO> loadCompleteProcess(OrderCommon order, String icId, Boolean isLoadComplete) {
        // 已达预装量
        boolean isReachPreBags = Objects.equals(order.getActualBags(), order.getBags() + Optional.ofNullable(order.getSupplementBags()).orElse(0));
        log.warn("装车完成!已达预定量:[{}] 订单号:[{}],IC卡号:[{}]", isReachPreBags, order.getOrder(), icId);
        // 更新订单状态
        IntegrationMsg msg = CarbonPanel.getFGRecordBuilder(order.getOrderTableKey(), String.valueOf(order.getId()))
//                .putAttribute(order.getOrderStateKey(), 订单记录_状态_待过二磅_dgeb)
                // 装车完成时,更新上次实际装货量
                .putAttribute(order.getLastLoadingVolumeKey(), order.getActualBags())
                .putAttribute(order.getOrderLoadCompleteKey(), isLoadComplete)
                .integrate();
        if (!msg.success()) {
            log.error("装车完成失败,订单号:[{}],IC卡号:[{}],原因:{}", order.getOrder(), icId, msg.getRefuseMsgContentStr());
            throw new BusinessException("装车完成失败,IC卡号:[" + icId + "],原因:" + msg.getRefuseMsgContentStr());
        }

        // 合并订单编织袋使用记录
        wovenBagRecordService.mergeWovenBagRecord(order.getId(), order.getOrder());

        log.warn("装车完成,订单号:[{}],IC卡号:[{}]", order.getOrder(), icId);
        String message = isReachPreBags ? "装车完成,请取走IC卡!" : "装车完成,但未达预装量,请插卡继续装车!";
        return Resp.success(message);
    }

    /**
     * 装车处理
     *
     * @param logPrefix 日志前缀
     * @param order     订单
     * @param param     请求参数
     * @return resp
     */
    private Resp<ClientInvokingRespVO> loadProcess(String logPrefix, OrderCommon order, ClientInvokingParam param) {
        if (!Objects.equals(订单记录_状态_待过二磅_dgeb, Long.valueOf(order.getState()))
                && !Objects.equals(PublicConstant.ProductPackageTypeBag, order.getPackTypeEN())) {
            log.error("{}当前状态or订单包装类型不允许装车", logPrefix);
            throw new BusinessException(Load_Not_Allow_Loading);
        }

        InstallationLaneVO laneVO = param.getInstallationLaneVO();
        // 验证订单类型(袋装or散装)对应的车道类型(袋装车道or散装车道)是否匹配
        if (!Objects.equals(laneVO.getProducePackagingTypeStr(), order.getPackTypeZH())) {
            log.error("{}订单包装类型:[{}]与车道类型:[{}]不匹配", logPrefix, order.getPackTypeZH(), laneVO.getProducePackagingTypeStr());
            throw new BusinessException(Load_Packaging_Type_Not_Match);
        }

        // 验证订单车辆类型与装车道适配的车辆类型是否匹配
        if (CollectionUtil.isEmpty(param.getInstallationLaneVO().getAdaptVehicleTypeStrList())
                || !param.getInstallationLaneVO().getAdaptVehicleTypeStrList().contains(order.getVehicleStructureTypeStr())) {
            log.error("{}订单车辆类型:[{}]与车道适配类型:[{}]不匹配", logPrefix, order.getVehicleStructureTypeStr(), laneVO.getApplicableVehiclesStr());
            throw new BusinessException(Load_Vehicle_Type_Not_Match);
        }

        // 验证订单关联的产品与车道适配的产品是否匹配
        if (CollUtil.isNotEmpty(laneVO.getGoodsList())) {
            if (!laneVO.getGoodsList().stream().map(Goods::getId).collect(Collectors.toSet())
                    .contains(Optional.ofNullable(order.getMaterialId()).orElse(0L))) {
                log.error("{}订单关联的产品:[{}]与车道适配的产品:[{}]不匹配", logPrefix, order.getMaterialId(), laneVO.getGoodsList());
                throw new BusinessException(Load_Product_Type_Not_Match);
            }
        } else {
            throw new BusinessException(Load_Product_List_Is_Empty);
        }

        if (Boolean.TRUE.equals(order.getIsLoadComplete())) {
            log.error("{}订单已装车完成,不允许继续装车!", logPrefix);
            throw new BusinessException(Load_Order_Already_Complete);
        }

        // 皮重缺失不允许装车
        Optional<WeighOther> tareWeighOptional = weighService.getTareWeighByOrderNo(order.getOrder(), order.getOrderTypeEn());
        if (!tareWeighOptional.isPresent()) {
            log.error("{}皮重缺失不允许装车!", logPrefix);
            throw new BusinessException(Load_Tare_Missing_Not_Allow);
        }

        // 实际装袋量
        Integer actualBags = Optional.ofNullable(order.getActualBags()).orElse(0);
        // 验证已装量=下单时预装量+补包数量  是否一致
        int preQuantity = order.getBags() + Optional.ofNullable(order.getSupplementBags()).orElse(0);
        if (actualBags.compareTo(preQuantity) == 0) {
            log.error("{}已装量[{}]=预装量[{}],不允许装车!", logPrefix, actualBags, preQuantity);
            throw new BusinessException(Load_Reach_Predetermined_Loading);
        } else if (actualBags.compareTo(preQuantity) > 0) {
            log.error("{}已超发[{}]袋,不允许装车!", logPrefix, (actualBags - preQuantity));
            throw new BusinessException(Load_Exceed_Delivery);
        } else {
            log.warn("允许装车");
            ClientInvokingRespVO resp = new ClientInvokingRespVO();
            resp.setOrderNo(order.getOrder());
            resp.setCarNumber(order.getVehicle());
            // 下单包数
            resp.setOrderPreBags(preQuantity);
            // 预装袋量 = 下单时预装量 + 补包数量 - 已装袋量
            resp.setPreBags(preQuantity - order.getActualBags());
            resp.setSupplementBags(order.getSupplementBags());
            // 设置已装袋数
            resp.setLoadedBag(order.getActualBags());
            // 设置物料名称,用于装车道展示
            resp.setMaterialName(order.getMaterialName());
            return Resp.success(Load_Allow_Loading, resp);
        }
    }

    /**
     * 称重处理
     *
     * @param order 订单
     * @param param 称重重量
     * @return resp
     */
    private Resp<ClientInvokingRespVO> weightProcess(String logPrefix, OrderCommon order, ClientInvokingParam param) {
        // 订单主键id
        String orderId = String.valueOf(order.getId());
        // 订单号
        String orderNo = order.getOrder();
        // 订单状态
        Long orderState = Long.valueOf(order.getState());
        // 订单类型
        String orderType = order.getOrderTypeEn();
        // 订单类型(中文描述)
        String orderTypeZh = order.getOrderTypeZh();
        BigDecimal weight = param.getWeight();
        // 重量
        Double weigh = Double.valueOf(weight.toString());
        // 车牌号
        String carNo = order.getVehicle();
        // 是倒料订单
        boolean isPouringOrder = PublicConstant.OrderTypePouring.equals(orderType);
        // 是过一磅
        boolean isOnePounds = Objects.equals(订单记录_状态_待过一磅_dgyb, orderState);
        // 是过二磅
        boolean isTwoPounds = Objects.equals(订单记录_状态_待过二磅_dgeb, orderState);
        boolean isBag = Objects.equals(PublicConstant.ProductPackageTypeBag, order.getPackTypeEN());
        // 称重响应参数
        ClientInvokingRespVO resp = new ClientInvokingRespVO();
        WeightingDTO dto = initWeightingDTO(logPrefix, order, weigh, param);

        // 完善日志
        logPrefix += ",订单类型:[" + orderTypeZh + "]";
        if (isOnePounds) {
            logPrefix += ",[过一磅称重]";
            if (PublicConstant.OrderTypeSale.equals(orderType)) {
                // 销售订单空车皮重校验
                orderTareCheck(weigh, dto, orderNo, carNo);
            }
            // 倒料订单校验皮重毛重
            if (isPouringOrder) {
                Resp<ClientInvokingRespVO> result = pouringTareCheck(logPrefix, dto, order, weigh);
                if (Objects.nonNull(result)) {
                    return result;
                }
            }
        } else if (isTwoPounds) {
            logPrefix += ",[过二磅称重]";
            if (PublicConstant.OrderTypeSale.equals(orderType)) {
                // 对比计划重量和实际重量是否在设置的误差范围内校验
                Resp<ClientInvokingRespVO> result = loadOverCheck(dto, order.getWeight(), orderNo, order.getPackTypeZH(), carNo, order.getExceptionPassOrNot());
                if (Objects.nonNull(result)) {
                    return result;
                }
                // 处理水泥批次号
                Resp<ClientInvokingRespVO> batchCheckResult = batchCementCheck(logPrefix, dto, order);
                if (Objects.nonNull(batchCheckResult)) {
                    return batchCheckResult;
                }
            } else {
                // 皮重不能比毛重重(否则设置为空车出厂)
                tareGreaterThanGrossCheck(dto, orderNo);
            }
        } else {
            return Resp.weighFail(WeighingResultEnum.System_Abnormal.getCode(), Weigh_Fail_Please_Lower_The_Scale);
        }
        log.warn("{}", logPrefix);

        if (Objects.nonNull(dto.getNet()) && Double.valueOf(0).compareTo(dto.getNet()) > 0) {
            log.warn("{}称重结果异常,称重结果:[{}]", logPrefix, dto.getNet());
            dto.setNet((double) 0);
        }

        log.info("order:{}", JSONUtil.toJsonStr(order));
        log.info("dto:{}", JSONUtil.toJsonStr(dto));

        // 更新订单状态和实际重量
        FGRecordBuilder orderBuilder = CarbonPanel.getFGRecordBuilder(dto.getTableKey(), orderId)
                .putAttribute(dto.getActualWeighKey(), dto.getNet())
                .putAttribute(dto.getOrderStateKey(), dto.getOrderStateValue());
        // 倒料过磅时设置订单信息
        if (isOnePounds && isPouringOrder) {
            if (Objects.nonNull(dto.getWeightTareNew())) {
                // 更新车辆最新皮重信息
                carService.updateTare(carNo, dto.getWeightTareNew());
                // 设置订单中车辆皮重更新时间字段
                orderBuilder.putAttribute(PouringOrderHWANE18490Item.基本属性组_皮重更新时间, LocalDateTime.now());
            }
            orderBuilder.putAttribute(PouringOrderHWANE18490Item.基本属性组_重量, dto.getNet());
        }
        IntegrationMsg buildMsg = CarbonPanel.getRecordIntegration(dto.getTableKey()).integrate(orderBuilder.getRecord());
        if (!buildMsg.success()) {
            log.error("{}更新订单状态失败,原因 => {}", logPrefix, buildMsg.getRefuseMsgContentStr());
            String errorMsg = carNo + " 当前净重:[" + dto.getNet() + "];原因:" + buildMsg.getRefuseMsgContentStr();
            weighService.saveFailWeighRecord(dto, orderNo, errorMsg, carNo, null, null);
            if (buildMsg.getRefuseMsgContentStr().contains("请充值")) {
                return Resp.weighFail(WeighingResultEnum.Customer_Balance_Insufficient.getCode(), "更新订单失败！原因：" + buildMsg.getRefuseMsgContentStr());
            }
            return Resp.weighFail(WeighingResultEnum.System_Abnormal.getCode(), "更新订单失败！原因：" + buildMsg.getRefuseMsgContentStr());
        }

        // 记录称重记录(称重编号由融合时生成,参考 WeighHWANE16720BNB.secondImprove 方法)
        WeighOther weighOtherRecord = new WeighOther();
        weighOtherRecord.setWeightTare(dto.getTare());
        if (isBag) {
            double v = BigDecimal.valueOf(Optional.ofNullable(dto.getTare()).orElse(0.0)).add(BigDecimal.valueOf(Optional.ofNullable(dto.getNet()).orElse(0.0))).doubleValue();
            weighOtherRecord.setWeightGross(v);
        } else {
            if (null != dto.getGross()) {
                weighOtherRecord.setWeightGross(dto.getGross());
            }
        }
        // 实际毛重量
        weighOtherRecord.setActualWeightGross(null != dto.getGross() ? new BigDecimal(dto.getGross().toString()) : null);
        weighOtherRecord.setWeight(dto.getNet());
        weighOtherRecord.setFrequency(dto.getFrequency());
        weighOtherRecord.setIsOk(true);
        weighOtherRecord.setOrderNumber(orderNo);
        weighOtherRecord.setCarNumber(carNo);
        weighOtherRecord.setProductName(order.getMaterialName());
        weighOtherRecord.setPassTime(LocalDateTime.now());
        if (PublicConstant.OrderTypeSupply.equals(orderType)) {
            if (isOnePounds) {
                weighOtherRecord.setSupplyOrderId1(order.getId());
            }else {
                weighOtherRecord.setSupplyOrderId2(order.getId());
            }
        }
        // 保存过磅成功信息
        IntegrationMsg msg = CarbonPanel.getRecordIntegration(过磅信息_gbxx).integrate(weighOtherRecord);
        if (!msg.success()) {
            log.error("{}称重记录保存失败,原因 => {}", logPrefix, msg.getRefuseMsgContentStr());
            throw new WeighException(WeighingResultEnum.System_Abnormal, "称重记录保存失败！原因：" + msg.getRefuseMsgContentStr());
        }

        resp.setOrderNo(orderNo);
        // 设置订单状态
        resp.setOrderStatusStr(OrderCommon.getStateValue(String.valueOf(dto.getOrderStateValue())));
        // 返回车牌号
        resp.setCarNumber(carNo);
        // 返回本次是皮重还是毛重(用于传抓拍照片)
        resp.setTareOrGross(dto.getTareOrGross());
        // 设置皮重
        resp.setTare(dto.getTare());
        // 设置毛重
        resp.setGross(dto.getGross());
        // 设置订单类型
        resp.setOrderTypeEn(order.getOrderTypeEn());
        // 设置订单车辆类型(用于过完一磅保存销售单袋装订单车道排队信息)
        resp.setVehicleType(order.getVehicleStructureTypeStr());
        // 设置订单包装类型
        resp.setPackType(order.getPackTypeZH());
        // 设置倒料订单类型
        resp.setPouringOrderType(order.getPouringOrderTypeEN());
        // 设置一磅称重时间
        if (isOnePounds) {
            resp.setFirstWeighTime(weighOtherRecord.getPassTime());
        }
        return Resp.success(Weigh_Success, resp);
    }

    public void saleOrderBatchPackageDateCheck(String logPrefix, OrderCommon order) {
        Optional<Config> batchCheckOptional = configService.getConfig(参数配置_类型_批号异常校验_phycxy);
        if (!batchCheckOptional.isPresent() || Objects.equals(batchCheckOptional.get().getValue(), "false")) {
            return;
        }
        // 查看启用的&指定类型的 批次中的剩余量是否满足订单中的量
        List<OrderCommon> saleOrderList = vehicleOrderService.getSaleOrderListByTypeBatchType(Collections.singletonList(String.valueOf(订单记录_状态_待过二磅_dgeb)), order.getType(), order.getBatchType());
        // 判断符合条件的剩余批次是否符合满足条件的订单的下单总重量
        BigDecimal number = CollectionUtil.isEmpty(saleOrderList) ? order.getWeight() : order.getWeight().add(saleOrderList.stream().map(OrderCommon::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add));
        List<Batch> currentAvaliableBatchList = batchService.getAvailableBatchList(order.getType(), order.getBatchType());
        BigDecimal sumResidue = CollectionUtil.isEmpty(currentAvaliableBatchList) ? BigDecimal.ZERO : currentAvaliableBatchList.stream().map(Batch::getSumResidue).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (CollectionUtil.isNotEmpty(currentAvaliableBatchList)) {
            if (number.compareTo(sumResidue) <= 0) {
                log.info("{}剩余批次满足本单扣除，不启用新批次！本次订单量：{}，当前启用批次剩余量：{}", logPrefix, number, sumResidue);
                return; // 剩余批次量满足本单扣除，不启用新批次
            }
        }
        log.warn("{}剩余批次不满足订单扣除，开始查询可启用批次！目标批次量：{}，当前启用批次剩余量：{}", logPrefix, number, sumResidue);

        List<Batch> toUpdateBatchList = batchService.getAvailableBatchListWithoutPackageDate(order.getType(), order.getBatchType());
        if (CollectionUtil.isEmpty(toUpdateBatchList)) {
            log.info("{}没有可更新的批次！本次订单量：{}", logPrefix, order.getWeight());
            throw new WeighException(WeighingResultEnum.Product_Number_Insufficient);
        }
        List<Batch> updateBatchList = new ArrayList<>();
        for (int i = 0; i < toUpdateBatchList.size(); i++) {
            Batch batch = toUpdateBatchList.get(i);
            sumResidue = sumResidue.add(batch.getSumResidue());
            updateBatchList.add(batch);
            if ((i == toUpdateBatchList.size() - 1) && number.compareTo(sumResidue) > 0) {
                log.info("{}可更新的批次不足！（{}）目标批次量：{}，当前启用+可启用批次剩余量：{}；本次订单量：{}", logPrefix, toUpdateBatchList.size(), number, sumResidue, order.getWeight());
                throw new WeighException(WeighingResultEnum.Product_Number_Insufficient);
            }
            if (number.compareTo(sumResidue) <= 0) {
                break;
            }
        }

        LocalDate now = LocalDate.now();
        updateBatchList.forEach(batch -> {
            batch.setPackageDate(now);
            batch.setFailureTime(now.plusDays(3));
        });
        batchService.updatePackageDateBatch(updateBatchList);
    }

    private Resp<ClientInvokingRespVO> batchCementCheck(String logPrefix, WeightingDTO dto, OrderCommon order) {
        // 这里只校验水泥批次
        boolean isCementBatch = Objects.equals(String.valueOf(批次类型_水泥批次_snpc), order.getBatchType());
        if (!isCementBatch) {
            log.info("{}不是水泥批次，不进行BatchCheck", logPrefix);
            return null;
        }

        // 更新时锁住批次信息,防止并发问题
        List<String> batchLockList = new ArrayList<>(2);

        synchronized (lockObj) {
            try {
                // 袋装扣减批次时按照下单重量扣除
                BigDecimal number = PublicConstant.ProductPackageTypeBag.equals(order.getPackTypeEN()) ? order.getWeight() : BigDecimal.valueOf(dto.getNet());
                Optional<Config> batchCheckOptional = configService.getConfig(参数配置_类型_批号异常校验_phycxy);
                List<Batch> batchList = batchService.getAvailableBatchList(order.getType(), order.getBatchType());
                log.error("{} 查询到的相关编号数据======{}", logPrefix, JSON.toJSONString(batchList));
                log.info("{} 批号异常校验参数：{}", logPrefix, batchCheckOptional);
                if (batchCheckOptional.isPresent() && Objects.equals(batchCheckOptional.get().getValue(), "true")) {
                    // 批号不足校验
                    if (CollectionUtil.isEmpty(batchList)
                            || number.compareTo(batchList.stream().map(Batch::getSumResidue).reduce(BigDecimal.ZERO, BigDecimal::add)) > 0) {
                        log.error("{} [{}]批号不足,请及时添加!", logPrefix, order.getPackTypeZH());
                        return Resp.weighFail(WeighingResultEnum.Product_Number_Insufficient);
                    }

                    // 遍历扣除批次
                    List<LoadingBatchRecord> batchRecordList = new ArrayList<>(2);
                    List<Batch> updateBatchList = new ArrayList<>(2);
                    boolean isBreak = false;
                    int sequence = 1;
                    for (Batch batch : batchList) {
                        if (LockUtil.containsKey(batch.getNumber())) {
                            log.error("{} [{}]批号[{}]已被占用,请稍后重试!", logPrefix, order.getPackTypeZH(), batch.getNumber());
                            return Resp.weighFail(WeighingResultEnum.System_Abnormal.getCode(), Batch_Occupied);
                        }

                        LoadingBatchRecord batchRecord = new LoadingBatchRecord();
                        batchRecord.setBatchNumber(batch.getNumber());
                        batchRecord.setCategoryId(String.valueOf(batch.getId()));
                        batchRecord.setCategoryName(batch.getNumber());
                        batchRecord.setLaneNo(dto.getLaneNo());
                        batchRecord.setLaneName(dto.getLaneName());
                        batchRecord.setLoadStartTime(Objects.nonNull(dto.getLoadStartTimeStr()) ? DateUtil.strToLocalDateTime(dto.getLoadStartTimeStr()) : null);
                        batchRecord.setLoadEndTime(Objects.nonNull(dto.getLoadEndTimeStr()) ? DateUtil.strToLocalDateTime(dto.getLoadEndTimeStr()) : null);
                        batchRecord.setOrderId(order.getId());
                        batchRecord.setDeductionSequence(sequence++);
                        // 添加一个参数字段 用于在BNB中区分是过磅新增还是PC页面新增,防止校验报错!!
                        batchRecord.setCommandParameters(装车批次记录命令参数_过磅新增_gbxz);

                        // 批次扣减量
                        BigDecimal subtractQuantity;
                        if (number.compareTo(batch.getSumResidue()) <= 0) {
                            batch.setSumResidue(batch.getSumResidue().subtract(number)); // 本单量<=本批次量  剩余量=剩余量-本次量
                            subtractQuantity = number;
                            number = BigDecimal.ZERO;   // 剩余无批次订单量=0
                            isBreak = true;
                        } else {
                            subtractQuantity = batch.getSumResidue();
                            number = number.subtract(batch.getSumResidue()); // 剩余无批次订单量-=本批次量
                            batch.setSumResidue(BigDecimal.ZERO); // 本单量>本批次量  剩余量=0
                        }
                        batchRecord.setQuantity(subtractQuantity);
                        batchRecordList.add(batchRecord);
                        updateBatchList.add(batch);
                        batchLockList.add(batch.getNumber()); // 添加批次编号锁
                        // 跳出遍历
                        if (isBreak) {
                            break;
                        }
                    }
                    LockUtil.setKeyBatch(batchLockList);
                    // 更新批次剩余量
                    batchService.updateSumResidueBatch(updateBatchList);
                    // 批次记录入库 batchRecordList
                    batchRecordService.saveBatch(batchRecordList);
                } else {
                    return null;
                }
            } catch (Exception e) {
                throw new BusinessException(e.getMessage());
            } finally {
                LockUtil.removeKeyBatch(batchLockList);
            }
        }

        return null;
    }

    private Resp<ClientInvokingRespVO> pouringTareCheck(String logPrefix, WeightingDTO dto, OrderCommon order, Double weigh) {
        BigDecimal weight = BigDecimal.valueOf(weigh);
        BigDecimal downTare = order.getWeightTare().subtract(order.getWeightTareDown());
        BigDecimal upTare = order.getWeightTare().add(order.getWeightTareUp());
        // 重量低于皮重最低误差时提示异常
        if (weight.compareTo(downTare) < 0) {
            String errorMsg = "车辆皮重下限:[" + order.getWeightTareDown() + "];车辆皮重:[" + order.getWeightTare() + "];当前皮重:[" + weigh + "];低于车辆最低皮重:[" + downTare + "];不允许下磅,请联系管理员处理!";
            log.error("{} {} {}", logPrefix, order.getVehicle(), errorMsg);
            // 设置异常类型和异常数量和装车道等信息
            weighService.saveFailWeighRecord(dto, order.getOrder(), errorMsg, order.getVehicle(), null, null);
            return Resp.weighFail(WeighingResultEnum.System_Abnormal.getCode(), Weigh_Pouring_Order_Tare_Verification_Exception);
        }
        if (weight.compareTo(downTare) >= 0 && weight.compareTo(upTare) <= 0) {
            boolean isTareChange = weight.compareTo(order.getWeightTareNew()) != 0;
            // 本次过磅为过皮重
            dto.setFrequency(2);
            // 皮重重量变化时更新最新皮重
            if (isTareChange) {
                dto.setWeightTareNew(weigh);
                dto.setTare(weigh); // 本次订单皮重也更新
                log.warn("{}倒料订单车辆最新皮重更新为:[{}]", logPrefix, weigh);
            }
            dto.setGross(null);
            dto.setNet(0.0);
            dto.setTareOrGross(0);

            // 再插入一条毛重过磅记录
            WeighOther grossWeighRecordOther = new WeighOther();
            grossWeighRecordOther.setWeightTare(dto.getTare());
            grossWeighRecordOther.setWeightGross(dto.getTare()); // 毛重=皮重
            grossWeighRecordOther.setWeight(dto.getNet());
            grossWeighRecordOther.setFrequency(1);
            grossWeighRecordOther.setIsOk(true);
            grossWeighRecordOther.setOrderNumber(order.getOrder());
            grossWeighRecordOther.setPassTime(LocalDateTime.now());
            grossWeighRecordOther.setActualWeightGross(null != dto.getGross() ? new BigDecimal(dto.getGross().toString()) : null);
            IntegrationMsg grossWeighMsg = CarbonPanel.getRecordIntegration(过磅信息_gbxx).integrate(grossWeighRecordOther);
            if (grossWeighMsg.success()) {
                log.warn("{}毛重记录保存成功", logPrefix);
            } else {
                log.error("{}毛重记录保存失败,原因 => {}", logPrefix, grossWeighMsg.getRefuseMsgContentStr());
                throw new WeighException(WeighingResultEnum.System_Abnormal, Weigh_Save_Fail_Wait_Admin_Process);
            }
        }
        if (weight.compareTo(upTare) > 0) {
            dto.setFrequency(1);
            // 本次过磅为毛重
            dto.setGross(weigh);
            dto.setNet(Double.valueOf(PublicMethod.subtractBigDecimal(weigh, dto.getTare()).toString())); // 净重 = 本次毛重 - 车辆最新皮重
            dto.setTareOrGross(1);

            // 再插入一条皮重过磅记录
            WeighOther tareWeighRecordOther = new WeighOther();
            tareWeighRecordOther.setWeightTare(dto.getTare());
            tareWeighRecordOther.setFrequency(2); // 皮重
            tareWeighRecordOther.setIsOk(true);
            tareWeighRecordOther.setOrderNumber(order.getOrder());
            tareWeighRecordOther.setPassTime(LocalDateTime.now());
            IntegrationMsg tareWeighMsg = CarbonPanel.getRecordIntegration(过磅信息_gbxx).integrate(tareWeighRecordOther);
            if (tareWeighMsg.success()) {
                log.warn("{}皮重记录保存成功", logPrefix);
            } else {
                log.error("{}皮重记录保存失败,原因 => {}", logPrefix, tareWeighMsg.getRefuseMsgContentStr());
                throw new WeighException(WeighingResultEnum.System_Abnormal, Weigh_Save_Fail_Wait_Admin_Process);
            }
        }
        return null;
    }

    private void tareGreaterThanGrossCheck(WeightingDTO dto, String orderNo) {
        Double tare = dto.getTare();
        Double gross = dto.getGross();

        if (Objects.nonNull(tare) && Objects.nonNull(gross) && tare.compareTo(gross) > 0) {
            log.warn("[{}]称重结果异常,皮重[{}]大于毛重[{}];设置净重为0,视为空车出厂!", orderNo, tare, gross);
            dto.setNet((double) 0);
        }
    }

    /**
     * 销售订单载货超装校验
     *
     * @param dto                过磅参数
     * @param planWeight         计划重量
     * @param orderNo            订单号
     * @param packTypeZH         订单包装类型(中文)
     * @param carNo              车牌号
     * @param exceptionPassOrNot 异常是否通过
     * @return 检查是否通过
     */
    private Resp<ClientInvokingRespVO> loadOverCheck(WeightingDTO dto, BigDecimal planWeight, String orderNo, String packTypeZH, String carNo, Boolean exceptionPassOrNot) {
        Optional<Config> netErrorMaxOptional = configService.getConfig(参数配置_类型_净重误差上限_jzwcsx);
        if (!netErrorMaxOptional.isPresent()) {
            return null;
        }

        Optional<OrderCommon> orderByOrderNo = vehicleOrderService.getNoCompletedOrderByOrderNo(orderNo);
        Optional<Config> customConfigOptional = configService.getConfig(参数配置_类型_自定义净重误差_zdyjzwc);
        Optional<Config> emptyLeavingConfigOptional = configService.getConfig(参数配置_类型_空车出厂误差_kcccwc);
        BigDecimal netErrorMax = WeightErrorCalculation.calculateNetWeightErrorValue(planWeight, customConfigOptional, netErrorMaxOptional.get());
        BigDecimal emptyErrMax = WeightErrorCalculation.calculateEmptyLeaving(emptyLeavingConfigOptional, netErrorMaxOptional.get(), planWeight);
        BigDecimal net = BigDecimal.valueOf(dto.getNet());
        log.warn("当前空车出厂值==={}",emptyErrMax);
        log.warn("当前净重值==={}",net);

        if (net.compareTo(emptyErrMax) <= 0) {
            // 如果净重小于0 && 净重绝对值大于0.2t 说明过磅净重数据异常了
            if (net.compareTo(BigDecimal.ZERO) < 0 && net.abs().compareTo(new BigDecimal("0.2")) >= 0) {
                return Resp.weighFail(WeighingResultEnum.System_Abnormal.getCode(), Weigh_Net_Weight_Abnormal);
            }
            // 空车出厂时净重设置为0,防止扣款
            dto.setNet(0.00);
            dto.setOrderStateValue(订单记录_状态_待归还卡_dghk);
            log.warn("下单量:[{}],当前净重:[{}],当前毛重:[{}],小于净重误差上限:[{}];设置净重为:0.00,空车出厂!", planWeight, dto.getNet(), dto.getGross(), emptyErrMax);
            return null;
        }

        // 是袋装
        boolean isBag = PublicConstant.ProductPackageTypeBagZH.equals(packTypeZH);
        // 单位
        String unit = isBag ? "袋" : "吨";
        Optional<Config> configOptional = configService.getConfig(参数配置_类型_单袋重量_ddzl);
        if (!configOptional.isPresent() || StringUtils.isBlank(configOptional.get().getValue())) {
            return Resp.weighFail(WeighingResultEnum.System_Abnormal.getCode(), Single_Bag_Weight_Parameter_Configuration_Missing);
        }
        BigDecimal singleWeigh = new BigDecimal(configOptional.get().getValue());

        // 袋装校验重量(散装只校验客户余额是否够即可)
        if (isBag) {
            Optional<Config> netErrorCheckOptional = configService.getConfig(参数配置_类型_净重误差校验_jzwcxy);
            boolean netCheck = netErrorCheckOptional.isPresent() && Objects.equals("true", netErrorCheckOptional.get().getValue());
            log.info("[误差校验]异常是否通过：{};是否超过误差范围：{}", exceptionPassOrNot, netCheck);
            // 判断载货重量是否在误差范围内
            if (net.compareTo(planWeight.add(netErrorMax)) > 0) {
                // 按照订单下单量计算(（实际净重-下单量）/误差千分比 = 需要退补的袋数)
                int overLoad = WeightErrorCalculation.calculateOverLoad(planWeight, net, singleWeigh);
                if (overLoad > 0) {
                    String errorMsg = "超过误差范围:[" + netErrorMax + "];下单量:[" + planWeight + "];当前净重:[" + dto.getNet() + "],超过最大计划重量:[" + planWeight.add(netErrorMax) + "];应退: " + overLoad + " " + unit;
                    log.error("{}", errorMsg);
                    // 设置异常类型和异常数量和装车道等信息
                    weighService.saveFailWeighRecord(dto, orderNo, errorMsg, carNo, 过磅异常类型_偏重_pz, overLoad);
                    if (!Objects.isNull(exceptionPassOrNot) && !exceptionPassOrNot && netCheck) {
                        // 超发后状态不变还是待过二磅,让现场人员处理完后再过二磅即可
                        ClientInvokingRespVO respVO = new ClientInvokingRespVO();
                        respVO.setOverBags(overLoad);

                        // 记录编织袋“退包”记录
                        wovenBagRecordService.save(dto.getOrderId(), orderNo, overLoad, 编织袋记录类型_退包_tb);

                        return Resp.weighFail(WeighingResultEnum.Net_Weight_Heavy, respVO);
                    }
                }
            }

            if (net.compareTo(planWeight.subtract(netErrorMax)) < 0) {
                // (（实际净重-下单量）/误差千分比 = 需要退补的袋数)
                int underLoad = WeightErrorCalculation.calculateUnderLoad(planWeight, net, singleWeigh);
                if (underLoad > 0) {
                    String errorMsg = "低于误差范围:[" + netErrorMax + "];下单量:[" + planWeight + "];当前净重:[" + dto.getNet() + "],低于最小计划净重:[" + planWeight.subtract(netErrorMax) + "];应补: " + underLoad + " " + unit;
                    log.error("{}", errorMsg);
                    // 设置异常类型和异常数量和装车道等信息
                    weighService.saveFailWeighRecord(dto, orderNo, errorMsg, carNo, 过磅异常类型_偏轻_pq, underLoad);

                    if (!Objects.isNull(exceptionPassOrNot) && !exceptionPassOrNot && netCheck) {
                        // 袋装时少发时,补重量重新上磅
                        ClientInvokingRespVO respVO = new ClientInvokingRespVO();
                        respVO.setLessBags(underLoad);
                        return Resp.weighFail(WeighingResultEnum.Net_Weight_Light, respVO);
                    }
                }
            }

            // 新需求:如果袋装误差校验通过设置净重为下单时的重量
            dto.setNet(planWeight.doubleValue());
            log.info("{} 袋装误差校验通过设置净重为下单时的重量!", orderNo);
        }

        return null;
    }

    /**
     * 销售订单空车皮重校验
     *
     * @param weigh   称重重量
     * @param dto     过磅参数
     * @param orderNo 订单号
     * @param carNo   车牌号
     * @return 检查是否通过
     */
    private void orderTareCheck(Double weigh, WeightingDTO dto, String orderNo, String carNo) {
        Optional<Config> configOptional = configService.getConfig(参数配置_类型_空车皮重上限_kcpzsx);
        if (!configOptional.isPresent()) {
            return;
        }

        Double tareMax = Double.valueOf(configOptional.get().getValue());
        if (weigh.compareTo(tareMax) > 0) {
            log.error("[空车皮重超过最大限制:[{}],当前皮重:[{}]", tareMax, weigh);
            String errorMsg = "车辆皮重:[" + weigh + "],超过最大限制:[" + tareMax + "]";
            weighService.saveFailWeighRecord(dto, orderNo, errorMsg, carNo, 过磅异常类型_偏重_pz, 0);
            throw new WeighException(WeighingResultEnum.System_Abnormal, Weigh_Empty_Truck_Tare_Exceed_Maximum_Limit);
        }

    }

    /**
     * 上磅处理
     *
     * @param logPrefix
     * @param order     订单
     * @return 错误信息
     */
    private Resp<ClientInvokingRespVO> onThePoundProcess(String logPrefix, OrderCommon order) {
        Long orderState = Long.valueOf(order.getState());
        if (Objects.equals(订单记录_状态_待过一磅_dgyb, orderState)) {
            // 校验:同一车号同一时间只能有一个过一磅的记录
            if (vehicleOrderService.hasWeighedOrWaiting(order.getVehicle(), order.getOrder())) {
                return Resp.weighFail(WeighingResultEnum.System_Abnormal.getCode(), Weigh_Vehicle_Has_Weighed_Or_Waiting_Weigh_Order);
            }
            synchronized (order.getOrder()) {
                // 这里只校验水泥批次
                boolean isCementBatch = Objects.equals(String.valueOf(批次类型_水泥批次_snpc), order.getBatchType());
                Optional<Config> batchCheckOptional = configService.getConfig(参数配置_类型_批号异常校验_phycxy);
                log.info("{}；是否为水泥批次：{}，批号异常校验：{}", logPrefix, isCementBatch, batchCheckOptional);
                if (isCementBatch && batchCheckOptional.isPresent() && Objects.equals(batchCheckOptional.get().getValue(), "true")) {
                    // 完善日志
                    logPrefix += ",订单类型:[" + order.getOrderTypeZh() + "]";
                    log.info("{}；开启水泥批次是否更新验证 ... ", logPrefix);
                    // 销售订单水泥批次号包装日期更新检查
                    this.saleOrderBatchPackageDateCheck(logPrefix, order);
                }
            }
        } else if (Objects.equals(订单记录_状态_待过二磅_dgeb, orderState)) {
            // [待过二磅]刷卡上磅
        } else {
            return Resp.weighFail(WeighingResultEnum.System_Abnormal.getCode(), Weigh_Not_Allow);
        }

        // 倒料订单校验车辆是否已设置车辆"皮重","最新皮重"
        if (Objects.equals(PublicConstant.OrderTypePouring, order.getOrderTypeEn())) {
            if (Objects.isNull(order.getWeightTare()) || BigDecimal.ZERO.equals(order.getWeightTare())) {
                return Resp.weighFail(WeighingResultEnum.System_Abnormal.getCode(), Weigh_Pouring_Vehicle_Not_Set_Tare);
            }
            if (Objects.isNull(order.getWeightTareNew()) || BigDecimal.ZERO.equals(order.getWeightTareNew())) {
                return Resp.weighFail(WeighingResultEnum.System_Abnormal.getCode(), Weigh_Pouring_Vehicle_Not_Set_Latest_Tare);
            }
        }

        ClientInvokingRespVO data = new ClientInvokingRespVO();
        // 车号返回用于led显示和语音提示
        data.setCarNumber(order.getVehicle());
        data.setOrderNo(order.getOrder());
        data.setOrderTypeEn(order.getOrderTypeEn());
        data.setOrderStatusStr(order.getStateStr());
        data.setPouringOrderType(order.getPouringOrderTypeEN());
        return Resp.success(Weigh_Allow, data);
    }

    /**
     * 初始化过磅参数
     *
     * @param logPrefix 日志前缀
     * @param weigh     称重重量
     * @param param     上磅请求参数
     * @return 过磅参数
     */
    private WeightingDTO initWeightingDTO(String logPrefix, OrderCommon order, Double weigh, ClientInvokingParam param) {
        WeightingDTO dto = new WeightingDTO();
        dto.setOrderId(order.getId());
        // 订单号
        String orderNo = order.getOrder();
        // 订单状态
        Long orderState = Long.valueOf(order.getState());
        // 订单类型
        String orderType = order.getOrderTypeEn();
        // 订单打包类型(袋装or散装)
        String orderPackageType = order.getType();
        // 是倒料订单
        boolean isPouringOrder = Objects.equals(PublicConstant.OrderTypePouring, orderType);
        // 是采购订单
        boolean isSaleOrder = PublicConstant.OrderTypeSale.equals(orderType);
        // 是供货订单
        boolean isSupplyOrder = PublicConstant.OrderTypeSupply.equals(orderType);
        logPrefix += ",订单:[" + orderNo + "]";

        // 设置车道相关信息
        if (Objects.nonNull(param.getLaneVO())) {
            dto.setLaneId(Objects.nonNull(param.getLaneVO().getId()) ? String.valueOf(param.getLaneVO().getId()) : null);
            dto.setLaneNo(param.getLaneVO().getLaneNumber());
            dto.setLaneName(param.getLaneVO().getLaneName());
            dto.setLoadStartTimeStr(param.getLaneVO().getLoadStartTimeStr());
            dto.setLoadEndTimeStr(param.getLaneVO().getLoadEndTimeStr());
        }

        if (isSaleOrder) {
            dto.setTableKey(订单记录_ddjl);
            dto.setOrderStateKey(OrderHWANE16677Item.基本属性组_状态);
            dto.setActualWeighKey(OrderHWANE16677Item.基本属性组_实际重量);
        } else {
            if (isSupplyOrder) {
                dto.setTableKey(供货订单_ghdd);
                dto.setOrderStateKey(SupplyOrderHWANE16829Item.基本属性组_状态);
                dto.setActualWeighKey(SupplyOrderHWANE16829Item.基本属性组_实际重量);
            } else {
                dto.setTableKey(倒料订单_dldd);
                dto.setOrderStateKey(PouringOrderHWANE18490Item.基本属性组_状态);
                dto.setActualWeighKey(PouringOrderHWANE18490Item.基本属性组_重量);
            }
        }

        if (Objects.equals(订单记录_状态_待过一磅_dgyb, orderState)) {
            dto.setFrequency(1);
            if (isPouringOrder) {
                // 倒料订单过完一磅直接结束
                dto.setOrderStateValue(订单记录_状态_已出厂_ycc);
            } else {
                // 销售订单过完一磅就可过二磅(不论袋装散装)
                // 散装过一磅后直接可过二磅; 倒料过一磅后可直接过二磅
                dto.setOrderStateValue(订单记录_状态_待过二磅_dgeb);
            }
            if (isSaleOrder) {
                dto.setTare(weigh);
                dto.setGross(null);
                dto.setNet(null); // 给0有问题
                dto.setTareOrGross(0); // 销售订单 & 倒料订单 一磅为皮重
                log.warn("{}一磅皮重:[{}]", logPrefix, weigh);
            } else {
                dto.setTare(isPouringOrder ? Double.valueOf(order.getWeightTareNew().toString()) : null); // 倒料订单皮重使用车辆最新皮重
                dto.setGross(weigh);
                dto.setNet(null); // 给0有问题
                dto.setTareOrGross(1); // 供货订单一磅为毛重
                log.warn("{}一磅毛重:[{}]", logPrefix, weigh);
            }
        } else {
            dto.setFrequency(2);
            if (isPouringOrder) {
                // 倒料订单过二磅后直接出厂
                dto.setOrderStateValue(订单记录_状态_已出厂_ycc);
            } else {
                dto.setOrderStateValue(订单记录_状态_待归还卡_dghk);
            }
            if (isSaleOrder) {
                //获取一磅皮重
                Optional<WeighOther> firstWeighRecordOption = weighService.getTareWeighByOrderNo(orderNo, orderType);
                if (!firstWeighRecordOption.isPresent()) {
                    log.error("{}皮重记录丢失", logPrefix);
                    throw new WeighException(WeighingResultEnum.System_Abnormal);
                }
                Double weightTare = firstWeighRecordOption.get().getWeightTare();
                //净重 = 毛重 - 皮重
                BigDecimal netWeigh = PublicMethod.subtractBigDecimal(weigh, weightTare);
                log.warn("{},一磅皮重:[{}],二磅毛重:[{}],二磅净重:{}", logPrefix, weightTare, weigh, netWeigh);
                dto.setTare(weightTare);
                dto.setGross(weigh);
                dto.setNet(Double.valueOf(netWeigh.toString()));
                dto.setTareOrGross(1); // 销售订单 & 倒料订单 二磅为皮重
            } else {
                //获取一磅毛重
                Optional<WeighOther> firstWeighRecordOption = weighService.getGrossWeighByOrderNo(orderNo, orderType);
                if (!firstWeighRecordOption.isPresent()) {
                    log.error("{}毛重记录丢失", logPrefix);
                    throw new WeighException(WeighingResultEnum.System_Abnormal);
                }
                Double weightGross = firstWeighRecordOption.get().getWeightGross();
                //净重 = 毛重 - 皮重
                BigDecimal netWeigh = PublicMethod.subtractBigDecimal(weightGross, weigh);
                log.warn("{},一磅毛重:[{}],二磅皮重:[{}],二磅净重:{}", logPrefix, weightGross, weigh, netWeigh);
                dto.setTare(weigh);
                dto.setGross(weightGross);
                dto.setNet(Double.valueOf(netWeigh.toString()));
                dto.setTareOrGross(0); // 供货订单二磅为皮重
            }
        }

        return dto;
    }

    /**
     * 前置机传参校验并返回订单信息
     *
     * @param param 前置机监听参数
     */
    private OrderCommon checkParamAndReturnOrder(ClientInvokingParam param) {
        boolean isWeight = Objects.equals(param.getCommand(), UP_WEIGHT) || Objects.equals(param.getCommand(), WEIGHT);
        // ic卡号缺失校验
        if (StrUtil.isBlank(param.getIcId()) && CollectionUtil.isEmpty(param.getIcCardList()) && StrUtil.isBlank(param.getCarNumber())) {
            if (isWeight) {
                throw new WeighException(WeighingResultEnum.System_Abnormal, Ic_Card_Number_Missing);
            } else {
                throw new BusinessException(Ic_Card_Number_Missing);
            }
        }

        Optional<OrderCommon> orderOptional = null;
        String icId = param.getIcId();
        String carNumber = param.getCarNumber();
        if (!StrUtil.isEmpty(icId)) {
            //根据IC卡号获取订单信息
            orderOptional = vehicleOrderService.getNoCompletedOrderByIcId(icId);
            if (!orderOptional.isPresent()) {
                if (isWeight) {
                    throw new WeighException(WeighingResultEnum.No_Pending_Order);
                } else {
                    throw new BusinessException(No_Order_Available);
                }
            }
        } else if (!StrUtil.isEmpty(carNumber)) {
            //根据车牌号获取订单信息
            orderOptional = vehicleOrderService.getNoCompletedOrderBycarNumber(carNumber);
            if (!orderOptional.isPresent()) {
                if (isWeight) {
                    throw new WeighException(WeighingResultEnum.No_Pending_Order);
                } else {
                    throw new BusinessException(No_Order_Available);
                }
            }
        }


        return orderOptional.get();
    }

    /**
     * 前置机称重图片上传接收
     *
     * @param imageVO 前置机抓拍的图片
     * @return resp
     */
    public Resp<String> uploadWeightPic(CaptureImageVO imageVO) {
        String logPrefix = "[前置机][称重抓拍图片上传]" + ",订单号:[" + imageVO.getOrderNo() + "]" + "[" + (0 == imageVO.getTareOrGross() ? "皮重" : "毛重") + "]";
        log.warn("{}[请求] => {}", logPrefix, JSONUtil.toJsonStr(imageVO));

        OrderCommon order;
        Optional<OrderCommon> orderOptional = vehicleOrderService.getNoCompletedOrderByOrderNo(imageVO.getOrderNo());
        if (!orderOptional.isPresent()) {
            // 判断是否为倒料订单(注意:倒料订单过一磅后状态直接变成已出厂了)
            Optional<OrderCommon> pouringOrderOptional = vehicleOrderService.getPouringOrderByOrderNo(imageVO.getOrderNo());
            if (!pouringOrderOptional.isPresent()) {
                log.error("{}未查询到订单信息", logPrefix);
                throw new BusinessException(Order_Information_Does_Not_Exist);
            }
            order = pouringOrderOptional.get();
        } else {
            order = orderOptional.get();
        }
        String orderType = order.getOrderTypeEn();

        // 称重记录
        Optional<WeighOther> weighOptional;
        // 中间变量字符串
        String middleStr;
        if (0 == imageVO.getTareOrGross()) {
            middleStr = PublicConstant.Weight_TarePic;
            weighOptional = weighService.getTareWeighByOrderNo(imageVO.getOrderNo(), orderType);
        } else if (1 == imageVO.getTareOrGross()) {
            middleStr = PublicConstant.Weight_GrossPic;
            weighOptional = weighService.getGrossWeighByOrderNo(imageVO.getOrderNo(), orderType);
        } else {
            log.error("{}参数缺失[称重类型]", logPrefix);
            throw new BusinessException(Weigh_Parameter_Missing_Weigh_Type);
        }
        if (!weighOptional.isPresent()) {
            log.error("{}未查询到称重记录", logPrefix);
            throw new BusinessException(Weigh_Record_Not_Found);
        }
        WeighOther weighOther = weighOptional.get();
        String weighNumber = weighOther.getNumber();

        // 根据称重抓拍相机编号决定设置字段值
        FGRecordBuilder fgRecordBuilder = CarbonPanel.getFGRecordBuilder(过磅信息_gbxx, String.valueOf(weighOther.getId()));

        try {
            // 磅前抓拍摄像机
            if (null != imageVO.getBeforeWeightImageFile()) {
                byte[] beforeWeightImageByte = imageVO.getBeforeWeightImageFile().getBytes();
                String beforeWeightImageFileName = weighNumber + middleStr + "前" + ".jpg";
                fgRecordBuilder.putAttribute(基本属性组_称重抓拍图片, new BytesInfoVO(beforeWeightImageFileName, beforeWeightImageByte));
            }

            // 磅中间抓拍摄像机
            if (null != imageVO.getMiddleWeightImageFile()) {
                byte[] middleWeightImageByte = imageVO.getMiddleWeightImageFile().getBytes();
                String middleWeightImageFileName = weighNumber + middleStr + "中" + ".jpg";
                fgRecordBuilder.putAttribute(基本属性组_称重抓拍图片2, new BytesInfoVO(middleWeightImageFileName, middleWeightImageByte));
            }

            // 磅后抓拍摄像机
            if (null != imageVO.getAfterWeightImageFile()) {
                byte[] afterWeightImageByte = imageVO.getAfterWeightImageFile().getBytes();
                String afterWeightImageFileName = weighNumber + middleStr + "后" + ".jpg";
                fgRecordBuilder.putAttribute(基本属性组_称重抓拍图片3, new BytesInfoVO(afterWeightImageFileName, afterWeightImageByte));
            }
        } catch (Exception e) {
            log.error("{}称重抓拍图片上传,获取图片byte数组异常:{}", logPrefix, e.getMessage());
            throw new BusinessException(Weigh_Picture_Upload_Parse_Error);
        }
        // 添加命令,跳过订单已完成不可修改称重记录验证
        fgRecordBuilder.putAttribute(基本属性组_命令, 操作命令_修改地磅称重图片_xgdbcztp);

        // 相关记录
        IntegrationMsg integrate = CarbonPanel.getRecordIntegration(过磅信息_gbxx).integrate(fgRecordBuilder.getRecord());
        Resp resp;
        if (integrate.success()) {
            resp = Resp.success("称重抓拍图片上传成功");
        } else {
            resp = Resp.fail("称重抓拍图片上传失败,原因:" + integrate.getRefuseMsgContentStr());
        }

        log.warn("{}[响应] => {}", logPrefix, JSONUtil.toJsonStr(resp));
        return resp;
    }

    /**
     * 根据ic卡号获取订单打印信息
     *
     * @param icCardNo         ic卡号
     * @param patchworkFlag    补打标记
     * @param patchworkOrderNo 补打订单号
     * @return 卡号关联订单小票打印信息
     */
    public Resp<WeightPrintInfoVO> getPrintInfoByIcCardNo(String icCardNo, Boolean patchworkFlag, String patchworkOrderNo) {
        String logPrefix = "[GCK_Front]前置机调用执行命令根据ic卡号获取订单打印信息;";
        log.warn("{}", logPrefix);

        Optional<OrderCommon> orderOptional;
        if (Boolean.TRUE.equals(patchworkFlag) && StrUtil.isNotBlank(patchworkOrderNo)) {
            orderOptional = vehicleOrderService.getOrderByNo(patchworkOrderNo);
            logPrefix += ";补打标记:[" + patchworkFlag + "];补打订单号:[" + patchworkOrderNo + "]";
        } else {
            orderOptional = vehicleOrderService.getNoCompletedOrderByIcId(icCardNo);
            logPrefix += ";订单号:[" + orderOptional.orElse(new OrderCommon()).getOrder() + "]";
        }
        if (!orderOptional.isPresent()) {
            log.error("{} => 未查询到订单信息!", logPrefix);
            throw new BusinessException("未查询到订单信息,IC卡号:[" + icCardNo + "]");
        }
        OrderCommon order = orderOptional.get();
        if (PublicConstant.OrderTypePouring.equals(order.getOrderTypeEn())) {
            log.error("{}", logPrefix);
            throw new BusinessException("当前订单类型[倒料订单]不允许打印小票,IC卡号:[" + icCardNo + "]");
        }

        // 设置皮重
        String orderNo = order.getOrder();
        String orderType = order.getOrderTypeEn();

        if (Boolean.TRUE.equals(patchworkFlag)) {
            if (!Objects.equals(订单记录_状态_已出厂_ycc, Long.valueOf(order.getState()))) {
                log.error("{} => 当前订单状态不允许打印小票!", logPrefix);
                throw new BusinessException(Not_Allow_Reissue);
            }
        } else {
            if (!Objects.equals(订单记录_状态_待归还卡_dghk, Long.valueOf(order.getState()))) {
                log.error("{} => 当前订单状态不允许打印小票!", logPrefix);
                throw new BusinessException("当前订单状态不允许打印小票,IC卡号:[" + icCardNo + "]");
            }
        }

        WeightPrintInfoVO printInfoVO = new WeightPrintInfoVO();
        // 车牌号
        printInfoVO.setCarNumber(order.getVehicle());
        // 设置产品规格
        printInfoVO.setProductSpec(order.getMaterialName());
        // 原材料产地
        printInfoVO.setMaterialPlace(order.getMaterialPlace());
        // 设置称重单位
        printInfoVO.setWeightUnit("吨");
        // 设置包装方式
        printInfoVO.setPackTypeZH(order.getPackTypeZH());
        printInfoVO.setPackTypeEN(order.getPackTypeEN());

        Double weightTare;
        Double weightGross;

        Optional<WeighOther> tareOptional = weighService.getTareWeighByOrderNo(orderNo, orderType);
        if (tareOptional.isPresent()) {
            weightTare = tareOptional.get().getWeightTare();
            printInfoVO.setTareWeight(String.valueOf(weightTare));
        } else {
            log.error("{} => 未查询到订单的皮重信息!", logPrefix);
            throw new BusinessException("未查询到订单号:[" + orderNo + "]的皮重信息");
        }
        // 设置毛重
        Optional<WeighOther> grossOptional = weighService.getGrossWeighByOrderNo(orderNo, orderType);
        if (grossOptional.isPresent()) {
            weightGross = grossOptional.get().getWeightGross();
            printInfoVO.setGrossWeight(String.valueOf(weightGross));
        } else {
            log.error("{} => 未查询到订单的毛重信息!", logPrefix);
            throw new BusinessException("未查询到订单号:[" + orderNo + "]的毛重信息");
        }
        // 是销售订单
        boolean isSaleOrder = PublicConstant.OrderTypeSale.equals(orderType);
        // 是散装
        boolean isBulk = PublicConstant.ProductPackageTypeBulk.equals(order.getPackTypeEN());
        // 是袋装
        boolean isBag = PublicConstant.ProductPackageTypeBag.equals(order.getPackTypeEN());
        // 设置净重
        BigDecimal net = BigDecimal.valueOf(weightGross).subtract(BigDecimal.valueOf(weightTare)).setScale(2, RoundingMode.HALF_UP);
        printInfoVO.setNetWeight(String.valueOf(net));
        // 设置进厂时间
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN);
        LocalDateTime enterTime = isSaleOrder ? tareOptional.get().getPassTime() : grossOptional.get().getPassTime();
        printInfoVO.setEnterTimeStr(enterTime.format(formatter));
        // 设置出厂时间
        LocalDateTime exitTime = isSaleOrder ? grossOptional.get().getPassTime() : tareOptional.get().getPassTime();
        printInfoVO.setExitTimeStr(exitTime.format(formatter));
        // 设置发货方式
        printInfoVO.setDeliveryMode(order.getCollectTypeStr());
        // 设置订单数量
        if (isSaleOrder && isBag) {
            printInfoVO.setQuantity(order.getBags());
        } else {
            printInfoVO.setQuantity(Optional.ofNullable(order.getWeight()).orElse(BigDecimal.ZERO).intValue());
        }
        // 设置备注
        printInfoVO.setRemark("-");
        printInfoVO.setOrderNo(order.getOrder());
        printInfoVO.setOrderTypeEn(order.getOrderTypeEn());
        printInfoVO.setOrderTypeZh(order.getOrderTypeZh());
        printInfoVO.setCompanyName(order.getCompanyName());
        printInfoVO.setTransporterName(order.getTransporterName());
        printInfoVO.setCarType(isSaleOrder ? "销售车辆" : "原料车辆");
        printInfoVO.setRechargeNumber(order.getRechargeNumber());
        printInfoVO.setBatchNumberNames(order.getBatchNumberNames());

        String tableKey;
        String orderStateKey;
        String isPrintKey;
        String printNumberKey;
        if (isSaleOrder) {
            tableKey = 订单记录_ddjl;
            orderStateKey = OrderHWANE16677Item.基本属性组_状态;
            isPrintKey = OrderHWANE16677Item.基本属性组_是否已打印小票;
            printNumberKey = OrderHWANE16677Item.基本属性组_小票打印次数;

            // 设置剩余吨数or袋数
            if (Objects.equals(订单记录_类型_袋装_dz, Long.valueOf(order.getType()))) {
                // 客商剩余袋数
                printInfoVO.setRemainNumber(BigDecimal.valueOf(Long.parseLong(Optional.ofNullable(order.getBagUsable()).orElse("0"))).intValue() + "/袋");
                // 装车袋数
                printInfoVO.setLoadingBags(Optional.ofNullable(order.getActualBags()).orElse(0) + "/袋");
            } else {
                printInfoVO.setRemainNumber(Optional.ofNullable(order.getWeightUsable()).orElse(0.00) + "/吨");
            }
        } else {
            tableKey = 供货订单_ghdd;
            orderStateKey = SupplyOrderHWANE16829Item.基本属性组_状态;
            isPrintKey = SupplyOrderHWANE16829Item.基本属性组_是否已打印小票;
            printNumberKey = SupplyOrderHWANE16829Item.基本属性组_小票打印次数;
        }
        // 修改订单状态
        FGRecordBuilder fgRecordBuilder = CarbonPanel.getFGRecordBuilder(tableKey, String.valueOf(order.getId()));
        if (Objects.isNull(patchworkFlag) || Boolean.FALSE.equals(patchworkFlag)) {
            fgRecordBuilder
                    .putAttribute(orderStateKey, 订单记录_状态_已出厂_ycc)
                    .putAttribute(isPrintKey, true);
        }
        // 修改打印次数
        fgRecordBuilder.putAttribute(printNumberKey, null == order.getReceiptPrintedNumber() ? 1 : order.getReceiptPrintedNumber() + 1);

        IntegrationMsg msg = fgRecordBuilder.integrate();
        if (!msg.success()) {
            log.error("{} => 修改订单状态失败!原因:{}", logPrefix, msg.getRefuseMsgContentStr());
            throw new BusinessException("打印订单信息时,修改订单状态失败,IC卡号:[" + icCardNo + "],原因:" + msg.getRefuseMsgContentStr());
        }

        Resp<WeightPrintInfoVO> success = Resp.success(printInfoVO);
        log.warn("{}[响应] => {}", logPrefix, JSONUtil.toJsonStr(success));
        return success;
    }

    /**
     * 根据订单号获取订单信息集合
     *
     * @return Resp<OrderCommon>
     * @orderNO 订单号
     */
    public Resp<List<OrderCommon>> getOrderInfoByOrderNo(List<String> orderNo) {
        log.warn("[GCK_Front]前置机调用获取订单信息[请求] => 订单号:[{}]", orderNo);
        Optional<List<OrderCommon>> orderOptional = vehicleOrderService.getOrderInfosByOrderNos(orderNo);
        if (!orderOptional.isPresent()) {
            return Resp.fail("订单不存在");
        }
        List<OrderCommon> order = orderOptional.get();

        Resp<List<OrderCommon>> success = Resp.success(order);
        log.warn("[GCK_Front]前置机调用获取订单信息[响应] => [{}]", JSONUtil.toJsonStr(success));
        return success;
    }


    /**
     * 根据订单号修改订单补包数
     *
     * @param carNO          车牌号
     * @param supplementBags 补包数
     * @return
     */
    public Resp<String> updateOrderSupplementBags(String carNO, int supplementBags) {
        CarbonPanel.changeCurrentProgram(Long.valueOf(LwdHttpConfig.programCode));
        log.warn("[GCK_Front]前置机调用修改订单补包数[请求] => 车牌号:[{}];补包数:[{}]", carNO, supplementBags);

        if (StrUtil.isBlank(carNO)) {
            throw new BusinessException("车牌号不能为空！");
        }
        //根据车牌号取出待过磅未结束订单信息
        Optional<Order> orderNoByCarNumber = vehicleOrderService.getOrderNoByCarNumber(carNO);
        if (!orderNoByCarNumber.isPresent()) {
            Resp<String> failResp = Resp.fail("订单异常");
            log.warn("[GCK_Front]前置机调用修改订单补包数[响应] => [{}]", JSONUtil.toJsonStr(failResp));
            return failResp;
        }
        Order order = orderNoByCarNumber.get();

        //修改补包数
        IntegrationMsg integrate = CarbonPanel.getFGRecordBuilder(订单记录_ddjl, String.valueOf(order.getId()))
                .putAttribute(OrderHWANE16677Item.基本属性组_补包数, order.getSupplementBags() + supplementBags)
                .putAttribute(OrderHWANE16677Item.基本属性组_装车完成标记, false)
                .integrate();

        // 记录编织袋“补包”记录
        wovenBagRecordService.save(order.getId(), order.getOrder(), supplementBags, 编织袋记录类型_补包_bb);

        Resp resp = new Resp(integrate.success() ? ResultCode.ONE : ResultCode.ZERO, integrate.getRefuseMsgContentStr());

        log.warn("[GCK_Front]前置机调用获取订单信息[响应] => [{}]", JSONUtil.toJsonStr(resp));
        return resp;
    }
}
